﻿/*
 * Copyright 2011 Hanns Kucer
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace iTachToolkit
{
    public class Proxy
    {
        private TubeDispatcher _dispatcher;
        private Thread _worker;
        private EndPoint _remote;
        private Recorder _recorder;

        public Proxy(EndPoint.IpEndPoint localEndPoint, EndPoint remoteEndPoint, bool record)
        {
            _dispatcher = new TubeDispatcher(localEndPoint);
            _remote = remoteEndPoint;

            // optionally start a recorder
            if(record)
                _recorder = new Recorder();
        }

		public delegate string TrancieveCallback(string data);

		// internal class for implementing callback based spoofing
		private class HookedEndPoint : EndPoint.VirtualEndPoint
		{
			private TrancieveCallback tc;

			private class HookediTach : iTach.VirtualiTach
			{
				private TrancieveCallback tc;

				public HookediTach(TrancieveCallback tc)
				{
					this.tc = tc;
				}

				public override string Trancieve(string data)
				{
					string res = tc(data);

					if(res != null)
						return res;
					else
				 		return base.Trancieve(data);
				}
			}

			public HookedEndPoint(TrancieveCallback tc)
			{
				this.tc = tc;
			}

			public override iTach iTachFactory()
			{
				return new HookediTach(tc);
			}
		}

		public Proxy(EndPoint.IpEndPoint localEndPoint, TrancieveCallback trancieveCallback, bool record)
			: this(localEndPoint, new HookedEndPoint(trancieveCallback), record)
		{
		}

        public void Start(bool block)
        {
            if (_recorder != null) _recorder.Start();
            _dispatcher.Start();

            if (block)
            {
                // cuurent thread will be the worker
                _worker = Thread.CurrentThread;
                _workerproc();
            }
            else
            {
                _worker = new Thread(_workerproc);
				_worker.Name = GetType().Name;
                _worker.Start();
            }
        }

        public void Stop()
        {
            _dispatcher.Stop();
            _worker.Join();
            if (_recorder != null) _recorder.Stop();
        }

        private void _workerproc()
        {
            while (true)
            {
                Tube tube = _dispatcher.WaitForConnection();

                // if dispatcher was stopped, we stop
                if (tube == null) break;

                // else, fork off a new handler for this tube
                new ClientDispatcher(_dispatcher, tube, _remote, _recorder);
            }
        }
    }
}
